from flask import Flask, request, jsonify
import requests
import re
from urllib.parse import urlparse
import logging
import time
from datetime import datetime
import json

app = Flask(__name__)
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

# 用于存储检测日志
detection_logs = []
MAX_LOG_ENTRIES = 1000  # 最大日志条目数

# 认证头部的关键词（小写）
AUTH_HEADERS = {
    'authorization', 'cookie', 'token', 'x-api-key', 'x-auth-token',
    'authentication', 'bearer', 'x-access-token', 'x-csrf-token',
    'x-requested-with', 'sessionid', 'jwt', 'apikey', 'api-key',
    'x-forwarded-for', 'x-real-ip'  # 这些可能包含认证信息
}


class DetectionLog:
    """检测日志类"""

    def __init__(self, timestamp, client_ip, url, method, is_unauthorized, details):
        self.timestamp = timestamp
        self.client_ip = client_ip
        self.url = url
        self.method = method
        self.is_unauthorized = is_unauthorized
        self.details = details

    def to_dict(self):
        return {
            'timestamp': self.timestamp,
            'client_ip': self.client_ip,
            'url': self.url,
            'method': self.method,
            'is_unauthorized': self.is_unauthorized,
            'details': self.details
        }


def add_detection_log(client_ip, url, method, is_unauthorized, details):
    """添加检测日志"""
    timestamp = datetime.now().isoformat()
    log_entry = DetectionLog(timestamp, client_ip, url, method, is_unauthorized, details)
    detection_logs.append(log_entry)

    # 限制日志数量
    if len(detection_logs) > MAX_LOG_ENTRIES:
        detection_logs.pop(0)

    logger.info(f"Detection log: {url} - Unauthorized: {is_unauthorized}")


def has_authentication_info(headers_dict):
    """
    检查请求头中是否包含认证信息
    """
    if not headers_dict:
        return False

    for header_name in headers_dict.keys():
        if header_name.lower() in AUTH_HEADERS:
            # 检查值是否非空且不是默认值
            value = headers_dict[header_name]
            if value and value.strip() and value not in ['null', 'undefined', 'none']:
                return True
    return False


def remove_authorization_headers(headers):
    """
    移除请求头中的认证信息
    """
    cleaned_headers = {}
    for key, value in headers.items():
        if key.lower() not in AUTH_HEADERS:
            cleaned_headers[key] = value
    return cleaned_headers


def parse_raw_http_request(raw_request):
    """
    解析原始HTTP请求
    """
    try:
        # 分割请求行和头部
        lines = raw_request.strip().split('\n')
        request_line = lines[0].strip()

        # 解析请求方法、路径和HTTP版本
        method, path, http_version = request_line.split()

        # 解析头部
        headers = {}
        body_start_index = 0
        for i, line in enumerate(lines[1:], 1):
            line = line.strip()
            if line == '':
                body_start_index = i + 1
                break
            if ':' in line:
                key, value = line.split(':', 1)
                headers[key.strip()] = value.strip()

        # 解析请求体
        body = '\n'.join(lines[body_start_index:]) if body_start_index < len(lines) else None

        # 获取完整的URL（从Host头构造）
        host = headers.get('Host', '')
        if not host:
            raise ValueError("Host header is required")

        # 构造完整URL
        if path.startswith('http'):
            url = path
        else:
            scheme = 'https' if headers.get('X-Forwarded-Proto') == 'https' or '443' in host else 'http'
            url = f"{scheme}://{host}{path}"

        return {
            'method': method,
            'url': url,
            'headers': headers,
            'body': body,
            'has_auth': has_authentication_info(headers)
        }

    except Exception as e:
        logger.error(f"Error parsing raw HTTP request: {e}")
        raise ValueError(f"Invalid HTTP request format: {e}")


def send_http_request(method, url, headers, body=None, timeout=10):
    """
    发送HTTP请求并返回响应
    """
    try:
        response = requests.request(
            method=method,
            url=url,
            headers=headers,
            data=body,
            timeout=timeout,
            verify=False,  # 忽略SSL证书验证
            allow_redirects=False  # 不自动重定向
        )

        return {
            'status_code': response.status_code,
            'headers': dict(response.headers),
            'body': response.text,
            'size': len(response.content)
        }

    except requests.exceptions.RequestException as e:
        logger.error(f"Request failed: {e}")
        return {'error': str(e)}


def compare_responses(original_response, unauthorized_response):
    """
    比较两个响应的差异
    """
    if 'error' in unauthorized_response:
        return {
            'match': False,
            'reason': f"Unauthorized request failed: {unauthorized_response['error']}"
        }

    # 比较状态码
    status_match = original_response['status_code'] == unauthorized_response['status_code'] == 200

    # 比较响应体长度（简单比较）
    size_similarity = abs(original_response['size'] - unauthorized_response['size']) / max(original_response['size'],
                                                                                           1) < 0.1

    # 比较响应体内容（简单文本比较）
    body_similarity = original_response['body'] == unauthorized_response['body']

    # 如果状态码相同且响应体高度相似，则认为可能存在未授权访问
    is_unauthorized = status_match and (body_similarity or size_similarity)

    return {
        'match': is_unauthorized,
        'status_match': status_match,
        'body_similarity': body_similarity,
        'size_similarity': size_similarity,
        'original_size': original_response['size'],
        'unauthorized_size': unauthorized_response['size']
    }


@app.route('/api/unauth-check', methods=['POST'])
def check_unauthorized_access():
    """
    API端点：检测未授权访问
    """
    try:
        # 获取客户端IP
        client_ip = request.remote_addr

        # 获取原始HTTP请求
        data = request.get_json()
        if not data or 'raw_request' not in data:
            return jsonify({'error': 'raw_request field is required'}), 400

        raw_request = data['raw_request']

        # 解析原始请求
        parsed_request = parse_raw_http_request(raw_request)

        # 前置检查：如果没有认证信息，直接返回
        if not parsed_request['has_auth']:
            logger.info(f"Skip check: No authentication info in request to {parsed_request['url']}")
            return jsonify({
                'skipped': True,
                'reason': 'No authentication information found in request',
                'url': parsed_request['url'],
                'method': parsed_request['method'],
                'message': '请求中未发现认证信息，跳过未授权检测'
            }), 200

        logger.info(f"Processing request with auth: {parsed_request['method']} {parsed_request['url']}")

        # 发送原始请求（带认证）
        original_response = send_http_request(
            parsed_request['method'],
            parsed_request['url'],
            parsed_request['headers'],
            parsed_request['body']
        )

        if 'error' in original_response:
            return jsonify({
                'error': f'Original request failed: {original_response["error"]}',
                'unauthorized_check': False
            }), 400

        # 移除认证头
        cleaned_headers = remove_authorization_headers(parsed_request['headers'])

        # 发送未认证请求
        unauthorized_response = send_http_request(
            parsed_request['method'],
            parsed_request['url'],
            cleaned_headers,
            parsed_request['body']
        )

        # 比较响应
        comparison = compare_responses(original_response, unauthorized_response)

        result = {
            'original_request': {
                'url': parsed_request['url'],
                'method': parsed_request['method'],
                'headers_count': len(parsed_request['headers']),
                'has_auth': True
            },
            'original_response': {
                'status_code': original_response['status_code'],
                'size': original_response['size']
            },
            'unauthorized_response': {
                'status_code': unauthorized_response.get('status_code', 'N/A'),
                'size': unauthorized_response.get('size', 0),
                'error': unauthorized_response.get('error')
            },
            'comparison': comparison,
            'is_unauthorized': comparison['match']
        }

        # 记录检测日志
        add_detection_log(
            client_ip=client_ip,
            url=parsed_request['url'],
            method=parsed_request['method'],
            is_unauthorized=comparison['match'],
            details={
                'original_status': original_response['status_code'],
                'unauthorized_status': unauthorized_response.get('status_code'),
                'comparison_result': comparison
            }
        )

        return jsonify(result)

    except ValueError as e:
        return jsonify({'error': str(e)}), 400
    except Exception as e:
        logger.error(f"Unexpected error: {e}")
        return jsonify({'error': 'Internal server error'}), 500


@app.route('/api/detection-logs', methods=['GET'])
def get_detection_logs():
    """
    API端点：获取检测日志
    """
    try:
        # 获取查询参数
        limit = int(request.args.get('limit', 50))
        offset = int(request.args.get('offset', 0))
        url_filter = request.args.get('url', '')
        method_filter = request.args.get('method', '')
        unauthorized_only = request.args.get('unauthorized_only', 'false').lower() == 'true'

        # 过滤日志
        filtered_logs = detection_logs

        if url_filter:
            filtered_logs = [log for log in filtered_logs if url_filter in log.url]

        if method_filter:
            filtered_logs = [log for log in filtered_logs if method_filter.upper() == log.method.upper()]

        if unauthorized_only:
            filtered_logs = [log for log in filtered_logs if log.is_unauthorized]

        # 分页
        total_count = len(filtered_logs)
        paginated_logs = filtered_logs[offset:offset + limit]

        # 转换为字典
        logs_data = [log.to_dict() for log in paginated_logs]

        return jsonify({
            'total_count': total_count,
            'returned_count': len(logs_data),
            'offset': offset,
            'limit': limit,
            'logs': logs_data
        })

    except Exception as e:
        logger.error(f"Error getting detection logs: {e}")
        return jsonify({'error': 'Internal server error'}), 500


@app.route('/api/detection-stats', methods=['GET'])
def get_detection_stats():
    """
    API端点：获取检测统计信息
    """
    try:
        total_detections = len(detection_logs)
        unauthorized_count = sum(1 for log in detection_logs if log.is_unauthorized)
        authorized_count = total_detections - unauthorized_count

        # 按方法统计
        method_stats = {}
        for log in detection_logs:
            method_stats[log.method] = method_stats.get(log.method, 0) + 1

        # 按结果统计
        result_stats = {
            'unauthorized': unauthorized_count,
            'authorized': authorized_count,
            'total': total_detections
        }

        return jsonify({
            'result_stats': result_stats,
            'method_stats': method_stats,
            'latest_detection': detection_logs[-1].to_dict() if detection_logs else None
        })

    except Exception as e:
        logger.error(f"Error getting detection stats: {e}")
        return jsonify({'error': 'Internal server error'}), 500


@app.route('/health', methods=['GET'])
def health_check():
    """健康检查端点"""
    return jsonify({
        'status': 'healthy',
        'log_count': len(detection_logs),
        'timestamp': datetime.now().isoformat()
    })


if __name__ == '__main__':
    # 禁用SSL警告
    import urllib3

    urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

    app.run(host='0.0.0.0', port=5000, debug=False)